package bev.article;

import java.util.Vector;

import java.awt.Component;

import javax.swing.JPanel;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.ParallelGroup;
import javax.swing.GroupLayout.SequentialGroup;
import javax.swing.JLabel;

/**
 * Used to display edit fields for editing an article's content.
 * 
 * @author Chris Marsh
 */
public class ArticleEditPanel extends JPanel {
    private static final long serialVersionUID = 648286090070305787L;

    private GroupLayout layout;
    private ParallelGroup hztlGroup;
    private SequentialGroup vertGroup;

    /**
     * A vector containing components to be added visually to the
     * ArticleEditPanel as an aligned group later when commitComponents() is
     * called.
     */
    private Vector<ComponentInfo> componentQueue;

    // These return the standard sizes for article editing components.
    /**
     * @return The standard size for <b>small</b> component widths for
     *         components on article edit panels.
     */
    public static int getSmallFieldSize() {
        return 100;
    }

    /**
     * @return The standard size for <b>medium</b> component widths for
     *         components on article edit panels.
     */
    public static int getMediumFieldSize() {
        return 250;
    }

    /**
     * @return The standard size for <b>large</b> component widths for
     *         components on article edit panels.
     */
    public static int getLargeFieldSize() {
        return 500;
    }

    /**
     * Instantiates a new ArticleEditPanel used to display edit fields for an
     * article when a user needs to edit the article.
     */
    public ArticleEditPanel() {
        layout = new GroupLayout( this );
        setLayout( layout );
        layout.setAutoCreateGaps( true );
        layout.setAutoCreateContainerGaps( true );

        // The horizontal parallel group to add new components to.
        hztlGroup = layout.createParallelGroup();
        layout.setHorizontalGroup( hztlGroup );

        // The vertical sequential group to add new components to.
        vertGroup = layout.createSequentialGroup();
        layout.setVerticalGroup( vertGroup );

        componentQueue = new Vector<ComponentInfo>();
    }

    /**
     * Adds a Swing component to the component commit queue for this
     * ArticleEditPanel and uses a preferred width for that component.
     * <p>
     * Note:
     * <p>
     * The component won't ACTUALLY be added to the ArticleEditPanel until
     * commitComponents() is called. This assures components added one after the
     * other can be spaced the same and align together as a group by waiting to
     * call commitComponents() after all the grouped components have been added.
     * 
     * @param label
     *            The label to be displayed to the left of the component.
     * @param comp
     *            The component to attach next to the ArticleEditPanel.
     * @param width
     *            The preferred width to use for the component. ArticleEditPanel
     *            provides standard widths that can be returned with calls such
     *            as getSmallFieldSize(), getMediumFieldSize(), etc.
     */
    public void addComponent( String label, Component comp, int width ) {
        componentQueue.add( new ComponentInfo( label, comp, width ) );
    }

    /**
     * Adds a Swing component to the component commit queue for this
     * ArticleEditPanel.
     * <p>
     * Note:
     * <p>
     * The component won't ACTUALLY be added to the ArticleEditPanel until
     * commitComponents() is called. This assures components added one after the
     * other can be spaced the same and align together as a group by waiting to
     * call commitComponents() after all the grouped components have been added.
     * 
     * @param label
     *            The label to be displayed to the left of the component.
     * @param comp
     *            The component to attach next to the ArticleEditPanel.
     */
    public void addComponent( String label, Component comp ) {
        componentQueue.add( new ComponentInfo( label, comp ) );
    }

    /**
     * Commits all of the components in the component commit queue, adding them
     * all as a group with the same alignment to the actual ArticleEditPanel.
     */
    public void commitComponents() {
        ParallelGroup hztlLabelGroup = layout
                .createParallelGroup( GroupLayout.Alignment.TRAILING );
        ParallelGroup hztlCompGroup = layout
                .createParallelGroup( GroupLayout.Alignment.LEADING );

        // Iterate through all of the components waiting in the component commit
        // queue and add them to the panel via the GroupLayout layout manager.
        for ( ComponentInfo ci : componentQueue ) {
            JLabel lbl = new JLabel( ci.label );

            hztlLabelGroup.addComponent( lbl );

            if ( ci.width != 0 )
                hztlCompGroup.addComponent( ci.comp,
                        GroupLayout.PREFERRED_SIZE, ci.width,
                        GroupLayout.PREFERRED_SIZE );
            else
                hztlCompGroup.addComponent( ci.comp );

            vertGroup.addGroup( layout
                    .createParallelGroup( GroupLayout.Alignment.BASELINE )
                    .addComponent( lbl ).addComponent( ci.comp ) );
        }

        hztlGroup.addGroup( layout.createSequentialGroup()
                .addGroup( hztlLabelGroup ).addGroup( hztlCompGroup ) );

        componentQueue.clear();
    }

    /**
     * Contains information about a component waiting in the queue to be added
     * to the ArticleEditPanel, such as its associated label, preferred width,
     * and a reference to the component itself.
     * 
     * @author Chris Marsh
     */
    class ComponentInfo {
        String label;
        Component comp;
        int width;

        public ComponentInfo( String label, Component comp, int width ) {
            this.label = label;
            this.comp = comp;
            this.width = width;
        }

        public ComponentInfo( String label, Component comp ) {
            this.label = label;
            this.comp = comp;
            this.width = 0;
        }
    }
}
